﻿using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;

namespace Swimj.Core.Extensions
{
    public static class ExpressionExtensions
    {
        public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
        {
            return Expression.Lambda<Func<T, bool>>
                (Expression.Not(Expression.Invoke(expr, expr.Parameters)), expr.Parameters);
        }

        public static MemberInfo? GetMemberInfo(this Expression expression)
        {
            if (expression is LambdaExpression lambda)
            {
                MemberExpression memberExpression;
                if (lambda.Body is UnaryExpression unaryExpression)
                {
                    memberExpression = (MemberExpression)unaryExpression.Operand;
                }
                else memberExpression = (MemberExpression)lambda.Body;

                return memberExpression.Member;
            }
            return null;
        }

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Not used in all design time assemblies.")]
        public static string GetMemberName<T>(this Expression<Func<T>> expr)
        {
            var body = expr.Body;
            if (body is MemberExpression || body is UnaryExpression)
            {
                MemberExpression memberExpression = body as MemberExpression ?? (MemberExpression)((UnaryExpression)body).Operand;
                return memberExpression.Member.Name;
            }

            return expr.ToString();
        }


        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Not used in all design time assemblies.")]
        public static string GetMemberName<T, TResult>(this Expression<Func<T, TResult>> expr)
        {
            var body = expr.Body;
            if (body is MemberExpression || body is UnaryExpression)
            {
                MemberExpression memberExpression = body as MemberExpression ?? (MemberExpression)((UnaryExpression)body).Operand;
                return memberExpression.Member.Name;
            }

            return expr.ToString();
        }

        public static PropertyInfo GetPropertyInfo(this Expression<Func<object>> expr)
        {
            Expression body = expr.Body;
            MemberExpression memberExpression = body as MemberExpression ?? (MemberExpression)((UnaryExpression)body).Operand;
            return (PropertyInfo)memberExpression.Member;
        }

        public static IDictionary<string, object> ReadParameters(this MethodCallExpression methodCallExpr)
        {
            ParameterInfo[] actionParameters = methodCallExpr.Method.GetParameters();
            MethodInfo dictionaryAdd = ((MethodCallExpression)((Expression<Action<Dictionary<string, object?>>>)(d => d.Add(string.Empty, null))).Body).Method;

            var result = new Dictionary<string, object>();
            if (actionParameters.Any())
            {
                var argExpression =
                    Expression.Lambda<Func<Dictionary<string, object>>>(
                        Expression.ListInit(Expression.New(typeof(Dictionary<string, object>)),
                            methodCallExpr.Arguments.Select((a, i) => Expression.ElementInit(dictionaryAdd, Expression.Constant(actionParameters[i].Name), Expression.Convert(a, typeof(object))))));

                var parameterValueGetter = argExpression.Compile();
                result = parameterValueGetter();
            }
            return result;
        }
    }
}
